/*
 * Copyright (C) 2020 Agrui
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ParameterCheck.h"
#include "OSMemory.h"
#include "IpcChannel.h"
#include "IpcChannelManager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>


#define SOCKET_IPC_PORT 8089
#define BUFFER_SIZE 4096
#define MAX_CLIENTS 5


typedef struct {
    IpcChannel channel;
    int clientFd;
    struct sockaddr_in address;
    pthread_t threadId;
} SocketClientChannel;


static void *socketWorker(void *parameter) {
    SocketClientChannel* channel = (SocketClientChannel*)parameter;
    char buffer[BUFFER_SIZE];
    int dateLen = 0;

    while(TRUE) {
        dateLen = read(channel->clientFd, buffer, sizeof(buffer));
        if (dateLen <= 0) {
            perror("read");
            break;
        }
        onIpcChannelDataReceive(channel->channel.channelId, buffer, dateLen);
    };
}


void socketClientInit(SocketClientChannel* channel) {
    int opt = 1;
    if ((channel->clientFd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        return;
    }

    channel->address.sin_family = AF_INET;
    channel->address.sin_addr.s_addr = INADDR_ANY;
    channel->address.sin_port = htons(SOCKET_IPC_PORT);

    if (connect(channel->clientFd, (struct sockaddr *)&channel->address, sizeof(channel->address)) < 0) {
        perror("Connection failed");
        close(channel->clientFd);
        return;
    }

    if (pthread_create(&channel->threadId, NULL, socketWorker, (void*)channel) != 0) {
        return;
    }

}

static void sendDataRequest(void* channelHandle, IpcDataHeadInfo* header, void* data, int dataLen) {
    SocketClientChannel* channel = (SocketClientChannel*)channelHandle;
    int8_t* buffer = (int8_t*)OS_MALLOC(sizeof(IpcDataHeadInfo) + dataLen);
    int8_t* temp = buffer;
    int bufferLen = 0;
    memcpy(temp, header, sizeof(IpcDataHeadInfo));
    bufferLen += sizeof(IpcDataHeadInfo);
    temp += sizeof(IpcDataHeadInfo);
    if (data && dataLen > 0) {
        memcpy(temp, data, dataLen);
        bufferLen += dataLen;
    }

    send(channel->clientFd, buffer, bufferLen, 0);
    OS_FREE(buffer);
}


static void channelSendDataHandle(void* channelHandle, void* data, int dataLen) {
    SocketClientChannel* channel = (SocketClientChannel*)channelHandle;
    send(channel->clientFd, data, dataLen, 0);
}

IpcChannel* newSocketClientChannel(int id) {
    SocketClientChannel* channel = OS_ALLOC_AND_RESET(SocketClientChannel);
    initIpcChannel(id, (IpcChannel*)channel);
    socketClientInit(channel);
    channel->channel.sendData = channelSendDataHandle;

    return (IpcChannel*)channel;
}


